Jx.declare('Jx.event.Listener', {

	$extend: 'Jx.lib.Hash',

	$category: 'event.listener.base',

	$actions: [

		'initialize'

	],

	$accessors: {

		handler: null,

		name: null,

		type: undefined,

		callback: null,

		scope: undefined,

		arguments: null,

		buffered: false,

		delay: 0,

		interval: 0,

		once: false,

		preventDefault: false,

		propagates: false

	},

	lastCall: null,

	callCount: 0,

	currentDelayId: undefined,

	strictAccess: true,

	delayedHandlers: null,

	delayedHandlersCount: 0,

	dispatcher: undefined,

	constructor: function(dispatcher, config){

		if (Jx.instanceOf(dispatcher, 'Jx.event.Dispatcher')){

			this.delayedHandlers = {};

			this.$super([ config ]);

			this.dispatcher = dispatcher;

			this.initialize(dispatcher);

		} else {

			throw new Error('invalid dispatcher');

		}

	},

	onInitialize: function(){

	},

	onAfterInitialize: function(){

		//-- create handler
		this.handler(

			this.createHandler()

		);

	},

	onDestroy: function(){

		var dispatcher = this.dispatcher;

		//-- stop delayed handlers
		Jx.each(this.delayHandlers, function(n, v, o){

			if (o.hasOwnProperty(n)){

				this.unregisterDelay(v);

			}

		}, this);

		//-- remove from dispatcher
		if (dispatcher){

			dispatcher.onDestroyListener( this );

		}


		this.$super(arguments);

	},

	onAfterCall: function(event){

		if (this.once()){

			this.destroy();

		}

	},

	onPostProcessCall: function(event){

		//-- process
		if (this.preventDefault()){

			event.preventDefault();

		}


		if (this.propagates()){

			event.propagates();

		}

	},

	registerDelay: function(handler, event, callback, delay){

		var timeout;

		handler.event = event;

		handler.callback = callback;

		handler.listener = this;

		this.delayedHandlers[

			timeout = handler.timeoutId = setTimeout(handler, delay)

		] = handler;

		this.delayedHandlersCount++;

		return timeout;

	},

	unregisterDelay: function(handler){

		var id = handler.timeoutId,

			dhs = this.delayedHandlers;

		if (dhs.hasOwnProperty(id)){

			this.delayedHandlersCount--;

			clearTimeout(id);

			delete this.delayedHandlers[id];

		}

	},

	allowSetPreventDefault: function(operation, value){

		return Jx.isBoolean(prevent);

	},

	allowSetPropagates: function(operation, value){

		return Jx.isBoolean(value);

	},

	createHandler: function(){

		//-- create wrapper from each accessors
		function EventHandler( listener, event ){

			var target = event.target(),
			
				dispatcher;

			if (!listener.isDestroyed() &&

				!(dispatcher = listener.dispatcher).isDestroyed() &&
				
				!target.isDestroyed()

			){

				dispatcher.onCallEnd(listener, event);

				//-- only called once
				if (!listener.isDestroyed() &&
					
					!target.isDestroyed()
					
				){

					listener.onPostProcessCall(event);

					listener.onAfterCall(event);

				}

			}

		}

		//-- create main event Handler
		return this.dispatcher.createEventHandler( this,  this.createWrapHandler(EventHandler) );

	},

	createWrapHandler: function(innerFn){

		var interval = parseInt(this.interval()),

			delay = parseInt(this.delay()),

			wrappedFn = innerFn;

		//-- create delay
		if (delay > 0){

			wrappedFn = this.createDelayHandler(innerFn, delay, this.buffered());

		}

		//-- create buffer
		if (interval > 0){

			wrappedFn = this.createIntervalHandler(innerFn, interval);

		}

		return wrappedFn;

	},

	createDelayHandler: function(innerFn, delay, buffered){

		function DelayEventHandler(listener, event){

			if (!buffered || !listener.delayedHandlersCount){

				function DelayHandler(){

					var me = arguments.callee,

						listener = me.listener;

					listener.unregisterDelay(me);

					me.callback(listener, me.event);

				}

				listener.registerDelay(DelayHandler, event, innerFn, delay);

			}

		}

		return DelayEventHandler;

	},

	createIntervalHandler: function(innerFn, interval){

		function IntervalEventHandler(listener, event){

			listener.callCount++;

			if (listener.callCount >= interval){

				delete listener.callCount;

				innerFn(listener, event);

			}

		}

		return IntervalEventHandler;


	}



});
